package com.dtb.vote_rank.servcie;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.dtb.vote_rank.cache.CacheSingleService;
import com.dtb.vote_rank.dao.VoteDao;
import com.dtb.vote_rank.entity.ProducerEntity;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import redis.clients.jedis.Tuple;

import java.util.*;

/**
 * @Author:ChengJian
 * @Description:
 * @Date: Created in 下午3:55 2018/11/15
 */
@Service
public class VoteService {

    @Autowired
    VoteDao voteDao;

    @Autowired
    CacheSingleService cacheService;

    public static final String RANK_ONE = "RANK_FIRST";

    public static final String RANK_TWO = "RANK_SECOND";

    public static final String RANK_THREE = "RANK_THREE";

    public static final String RANK_MOVE = "RANK_INFLUENCE_MOVE";

    //投票数量
    public static final String CACHE_RANK_VOTES_KEY = "CACHE_DTB_RANK_VOTES_";

    public static final String CACHE_HASH_ONE = "CACHE_HASH_FIRST";

    public static final String CACHE_HASH_TWO = "CACHE_HASH_SECOND";

    public static final String CACHE_HASH_THREE = "CACHE_HASH_THREE";

    /**
     *
     * @Description：小工具类
     * @Date：2018/11/20 下午5:00
     * @Author：ChengJian
     * @UpdateRemark:
     * @Version:1.0
     *
     */
    public Map<String,String> getResult(int type){
        Map<String,String> result = new HashMap<>();
        String rankKey            = null;
        String hashKey            = null;
        switch (type){
            case 1:
                rankKey = RANK_ONE;
                hashKey = CACHE_HASH_ONE;
                break;
            case 2:
                rankKey = RANK_TWO;
                hashKey = CACHE_HASH_TWO;
                break;
            case 3:
                rankKey = RANK_THREE;
                hashKey = CACHE_HASH_THREE;
                break;
            default:
                break;
        }
        result.put("rankKey",rankKey);
        result.put("hashKey",hashKey);
        return result;
    }

    /**
     *
     * @Description：获取排行位数
     * @Date：2018/11/20 下午5:55
     * @Author：ChengJian
     * @UpdateRemark:
     * @Version:1.0
     *
     */
    public long getRankIndex(int type,int id){
        String rankKey = getResult(type).get("rankKey");
        long rank      = cacheService.zrevrank(rankKey,id+"");
        rank++;
        return rank;
    }

    /**
     *
     * @Description：获取投票信息
     * @Date：2018/11/20 下午6:00
     * @Author：ChengJian
     * @UpdateRemark:
     * @Version:1.0
     *
     */
    public ProducerEntity getVoteInfo(int id,int type){

        Map<String,String> resuMap = getResult(type);
        String rankKey             = resuMap.get("rankKey");
        String hashKey             = resuMap.get("hashKey");
        ProducerEntity producer    = null;

        String result  = cacheService.hget(hashKey,id+"");

        if (StringUtils.isEmpty(result)){
            producer = voteDao.getProducer(id);
            if (producer != null){
                addRank(producer,type);
            }
        }else {
            producer = JSON.parseObject(result,ProducerEntity.class);
        }
        long rank = cacheService.zrevrank(rankKey,producer.getId()+"");
        rank++;
        producer.setRank(rank);

        return producer;
    }

    /**
     *
     * @Description：将参选目标加入到排行榜
     * @Date：2018/11/20 下午6:00
     * @Author：ChengJian
     * @UpdateRemark:
     * @Version:1.0
     *
     */
    public boolean addRank(ProducerEntity producer,int type){

        Map<String,String> map = getResult(1);

        String rankKey = map.get("rankKey");
        String hashKey = map.get("hashKey");
        boolean result = false;
        String field   = producer.getId()+"";
        int influence  = producer.getInfluencePower();
        long votes     = producer.getVoteNum();
        votes         += influence;
        String key     = CACHE_RANK_VOTES_KEY+field;

        try{
            //初始化投票数量
            cacheService.set(key,String.valueOf(votes));
            producer.setVoteNum(votes);
            cacheService.hset(hashKey,field,JSONObject.toJSONString(producer));
            cacheService.zadd(rankKey,votes,field);
            //入库
            producer.setVoteNum(votes);
            producer.setInfluencePower(influence);
            update(rankKey,producer);
            result =true;
        }catch (Exception e){
            e.printStackTrace();
        }
        return result;
    }

    /**
     *
     * @Description：根据票数获得排名并更新入库
     * @Date：2018/11/20 下午6:01
     * @Author：ChengJian
     * @UpdateRemark:
     * @Version:1.0
     *
     */
    public void update(String rankKey,ProducerEntity producerEntity){
        long rank = cacheService.zrevrank(rankKey,producerEntity.getId()+"");
        rank++;
        producerEntity.setRank(rank);
        voteDao.updateProduer(producerEntity);
    }

    /**
     *
     * @Description：更新缓存到数据库
     * @Date：2018/11/20 下午6:01
     * @Author：ChengJian
     * @UpdateRemark:
     * @Version:1.0
     *
     */
    public ProducerEntity updateMysql(ProducerEntity producerEntity){
        voteDao.updateProduer(producerEntity);
        return voteDao.getNewProducer(producerEntity.getName(),producerEntity.getCode());
    }

    /**
     *
     * @Description：投票
     * @Date：2018/11/20 下午6:01
     * @Author：ChengJian
     * @UpdateRemark:
     * @Version:1.0
     *
     */
    public boolean vote(int id,int voteNum,boolean isVote,int type){

        Map<String,String> map = getResult(type);

        String rankKey = map.get("rankKey");
        String hashKey = map.get("hashKey");
        boolean result = false;
        String key     = CACHE_RANK_VOTES_KEY+id;

        try {
            long votes  = cacheService.incrBy(key,voteNum);
            String json = cacheService.hget(hashKey,id+"");

            ProducerEntity producerEntity = JSONObject.parseObject(json,ProducerEntity.class);

            int influence = producerEntity.getInfluencePower();
            if (!isVote){
                influence+=voteNum;
                producerEntity.setInfluencePower(influence);
            }
            producerEntity.setVoteNum(votes);
            cacheService.hset(hashKey,producerEntity.getId()+"",JSONObject.toJSONString(producerEntity));
            cacheService.zadd(rankKey,votes,producerEntity.getId()+"");
            update(rankKey,producerEntity);
            result =true;
        }catch (Exception e){
            e.printStackTrace();
        }
        return result;
    }

    /**
     *
     * @Description：从排行榜中删除
     * @Date：2018/11/21 上午9:29
     * @Author：ChengJian
     * @UpdateRemark:
     * @Version:1.0
     *
     */
    public boolean removeRank(int type,int id){
        boolean result = false;
        String rankKey = getResult(type).get("rankKey");
        String hashKey = getResult(type).get("hashKey");
        //从hash中删除
        cacheService.hdel(hashKey,id+"");
        cacheService.del(CACHE_RANK_VOTES_KEY+id+"");
        //移除到另外一个队列中
        cacheService.smove(rankKey,RANK_MOVE,id+"");
        //删除排行
        cacheService.zrem(rankKey,id+"");
        result = true;
        return result;
    }

    /**
     *
     * @Description：随机指定范围内N个不重复的数 利用HashSet的特征，只能存放不同的值
     * @Date：2018/11/21 上午10:06
     * @Author：ChengJian
     * @UpdateRemark:min范围最小值，max范围最大值，n随机个数，set随机结果集
     * @Version:1.0
     *
     */
    private void randomSet(int min, int max, int n, HashSet<Integer> set) {
        if (n > (max - min + 1) || max < min) {
            return;
        }
        if(set.size()==n){
            return;
        }
        int num = (int) (Math.random() * (max - min)) + min;
        if(!set.contains(num)){
            set.add(num);
        }
        // 如果存入的数小于指定生成的个数，则调用递归再生成剩余个数的随机数，如此循环，直到达到指定大小
        if (set.size() < n) {
            randomSet(min, max, n , set);// 递归
        }
    }

    /**
     *
     * @Description：随机获取排行榜 count:大小
     * @Date：2018/11/21 上午10:16
     * @Author：ChengJian
     * @UpdateRemark:
     * @Version:1.0
     *
     */
    public List<ProducerEntity> getRandom(int type,int count,Set<String> voteIdSet){
        List<ProducerEntity> list = new ArrayList<>();
        Map<String,String> map    = getResult(type);
        String rankKey            = map.get("rankKey");
        String hashKey            = map.get("hashKey");
        Set<String> set           = cacheService.zrange(rankKey,0,-1);
        long hlen                 = cacheService.hlen(hashKey);

        ProducerEntity producer   = null;
        String jsonStr,id;
        long rank = 0;

        if (set.size() <= count && hlen >0){
            for (String field : set){
               jsonStr = cacheService.hget(hashKey,field);
               if (StringUtils.isEmpty(jsonStr)){
                   continue;
               }
               producer = JSONObject.parseObject(jsonStr,ProducerEntity.class);
               id       = producer.getId()+"";
               //匹配今日投票的ID
               if (voteIdSet != null && voteIdSet.contains(id+"")){
                    producer.setIsVote(1);
               }else {
                    producer.setIsVote(0);
               }
               rank = cacheService.zrevrank(rankKey,id+"");
               rank++;
               producer.setRank(rank);
               list.add(producer);
            }
        }else {
            HashSet<Integer> inSet = new HashSet<>();
            randomSet(1,set.size(),count,inSet);
            Object[] objArr      = set.toArray();
            Object[] indexSetArr = inSet.toArray();
            String field;
            int index;
            for (int i = 0;i<indexSetArr.length;i++){
                index   = (int) indexSetArr[i];
                field   = objArr[index].toString();
                jsonStr = cacheService.hget(hashKey,field);
                if (StringUtils.isEmpty(jsonStr)){
                    continue;
                }
                producer = JSONObject.parseObject(jsonStr,ProducerEntity.class);
                id       = producer.getId()+"";
                //匹配今日投票的ID
                if (voteIdSet != null && voteIdSet.contains(id)){
                    producer.setIsVote(1);
                }else {
                    producer.setIsVote(0);
                }
                rank = cacheService.zrevrank(rankKey,id);
                rank++;
                producer.setRank(rank);
                list.add(producer);
            }
        }
        return list;
    }

    /**
     *
     * @Description：获取排行榜前几名
     * @Date：2018/11/21 上午10:53
     * @Author：ChengJian
     * @UpdateRemark:
     * @Version:1.0
     *
     */
    public JSONObject getTop(int type,int page,int pageSize,String votedAttendId){

        JSONObject result         = new JSONObject();
        List<ProducerEntity> list = new ArrayList<>();

        Set<Tuple> set         = null;
        Map<String,String> map = getResult(type);
        String rankKey         = map.get("rankKey");
        String hashKey         = map.get("hashKey");
        int start              = page*pageSize;
        int end                = (page+1)*pageSize-1;
        long totalCounts       = cacheService.zcard(rankKey);
        int totalPage          = 0;

        if (totalCounts > 0 && totalCounts <= pageSize) {
            totalPage = 1;
        } else {
            totalPage = (int) (totalCounts / pageSize);
            if (totalPage * pageSize < totalCounts) {
                totalPage += 1;
            }
        }
        result.put("type", type);
        result.put("totalCounts", totalCounts);
        result.put("totalPage", totalPage);

        set = cacheService.zrevrangeWithScorese(rankKey,start,end);
        String field   = null;
        String jsonStr = null;
        String id = null;
        ProducerEntity producer = null;
        long rank = 0;
        if (set != null && set.size() >0){
            for (Tuple t : set){
                field   = t.getElement();
                jsonStr = cacheService.hget(hashKey,field);
                if (StringUtils.isEmpty(jsonStr)){
                    continue;
                }

                producer = JSONObject.parseObject(jsonStr,ProducerEntity.class);
                id       = producer.getId()+"";
                //匹配今日投票ID
                if (!StringUtils.isEmpty(votedAttendId) && id.equals(votedAttendId)){
                    producer.setIsVote(1);
                }else {
                    producer.setIsVote(0);
                }
                rank = cacheService.zrevrank(rankKey,id);
                rank++;
                producer.setRank(rank);
                list.add(producer);
            }
        }

        result.put("list",list);

        return result;
    }
    public ProducerEntity addProducer(ProducerEntity producerEntity){
        voteDao.addProducer(producerEntity);
        return voteDao.getNewProducer(producerEntity.getName(),producerEntity.getCode());
    }

    public ProducerEntity getProduce(int id){
        return voteDao.getProducer(id);
    }

    public void deleteProducer(int id){
        voteDao.delete(id);
    }

    public List<ProducerEntity> getList(Map<String,Object> map){
        return voteDao.getList(map);
    }
}
